package org.calzz.rpc.controller;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.calzz.rpc.JsonServiceExporter;
import org.calzz.rpc.exporter.RemoteMethodInfo;
import org.calzz.rpc.exporter.RemoteParameterInfo;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author Marek Brodziak <marek.brodziak@gmail.com>
 * @author Piotr Pejas <piotr.pejas@gmail.com>
 * @since Mar 8, 2008
 */
public class JsonServiceDescription {

	private static final String SYSTEM_CONTROLLER = "system";

	private String sdversion = "1.0";
	private String id = UUID.randomUUID().toString();
	private String name;
	private List<JsonProcedureDescription> procs = new LinkedList<JsonProcedureDescription>();

	/**
	 * A String value that provides a simple name for the method.
	 * @return service simple name
	 */
	public String getName() {
		return name;
	}

	/**
	 * A String value that provides a simple name for the method
	 * @param name Simple name to set;
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * An Array value whose element contain Service Procedure Description
	 * objects, each of uniquely describes a single procedure. If the only
	 * description of each procedure that a service has is its name, then it MAY
	 * instead supply an Array of String elements for this member and where each
	 * element uniquely names a procedure.
	 * @return list of Service Procedure Description
	 */
	public List<JsonProcedureDescription> getProcs() {
		return procs;
	}

	/**
	 * Add the Service Procedure Description to procedure list.
	 * @param procedureDescription The Service Procedure Description
	 */
	public void addProcedureDescription(JsonProcedureDescription procedureDescription) {
		procs.add(procedureDescription);
	}

	/**
	 * A String value that represents the version number of this object and MUST
	 * read "1.0" for conforming implementations.
	 * @param sdversion the object version number to set
	 */
	public void setSdversion(String sdversion) {
		this.sdversion = sdversion;
	}

	/**
	 * A String value that represents the version number of this object and MUST
	 * read "1.0" for conforming implementations.
	 * @return version number of this object.
	 */
	public String getSdversion() {
		return sdversion;
	}

	/**
	 * A String value that uniquely and globally identifies the service. The
	 * string MUST use the URI Generic Syntax (RFC 3986). Default value is
	 * random auto generated {@link UUID#randomUUID()}
	 * @return the service URI
	 */
	public String getId() {
		return id;
	}

	/**
	 * A String value that uniquely and globally identifies the service. The
	 * string MUST use the URI Generic Syntax (RFC 3986).
	 * @param id the service URI to set
	 */
	public void setId(String id) {
		this.id = id;
	}

	/**
	 * After autowiring a given service exporter its description is being created.
	 * @param exporter <code>JsonServiceExporter</code> bean
	 */
	@Autowired
	public void setJsonServiceExporter(JsonServiceExporter exporter) {

		this.name = exporter.getServiceName();

		Map<String, Map<String, RemoteMethodInfo>> controllers = exporter.getControllers();
		for (String controllerName : controllers.keySet()) {

			//don't index the system controller
			if (SYSTEM_CONTROLLER.equals(controllerName)) {
				continue;
			}

			Map<String, RemoteMethodInfo> methods = controllers.get(controllerName);

			for (String methodName : methods.keySet()) {

				RemoteMethodInfo methodInfo = methods.get(methodName);

				JsonProcedureDescription procedureDescription = new JsonProcedureDescription();
				procedureDescription.setName(controllerName + "." + methodName);
				procedureDescription.setReturnType(methodInfo.getMethod().getReturnType().toString());

				for (RemoteParameterInfo paramInfo : methodInfo.getParameters()) {
					if (paramInfo.isRemote()) {
						JsonParameterDescription paramDescription = new JsonParameterDescription(paramInfo.getName(), paramInfo.getClazz().toString());
						paramDescription.setRequired(paramInfo.isRequired());
						procedureDescription.addParams(paramDescription);
					}
				}

				addProcedureDescription(procedureDescription);
			}
		}
	}

}
